#!/bin/sh

########################################################################
# This test checks that the simple trace recorder subsystem records the
# events correctly if there are more of them that fit in the output buffer
# and the buffer pointers wrap around it.
# 
# Usage: 
#   sh test_buffer_wrap.sh
########################################################################

# Just in case the tools like lsmod are not in their usual location.
export PATH=$PATH:/sbin:/bin:/usr/bin

########################################################################
# A function to check prerequisites: whether the necessary files exist,
# etc.
########################################################################
checkPrereqs()
{
	if test ! -f "${CORE_MODULE}"; then
		printf "The core module is missing: ${CORE_MODULE}\n"
		exit 1
	fi

	if test ! -f "${FH_COMMON_MODULE}"; then
		printf "The module is missing: ${FH_COMMON_MODULE}\n"
		exit 1
	fi

	if test ! -f "${FH_CDEV_MODULE}"; then
		printf "The module is missing: ${FH_CDEV_MODULE}\n"
		exit 1
	fi

	if test ! -f "${TARGET_MODULE_SAMPLE}"; then
		printf "The target module is missing: ${TARGET_MODULE_SAMPLE}\n"
		exit 1
	fi

	if test ! -f "${OUTPUT_MODULE}"; then
		printf "Module is missing: ${OUTPUT_MODULE}\n"
		exit 1
	fi
	
	if test ! -f "${OUTPUT_RECORDER}"; then
		printf "The user-space part of the output system is missing: "
		printf "${OUTPUT_RECORDER}\n"
		exit 1
	fi
}

########################################################################
cleanupTargets()
{
	printf "Cleaning up the targets...\n"

	lsmod | grep "${TARGET_NAME_SAMPLE}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_SAMPLE}"
	fi
}

########################################################################
# is_process_running pid
# True if the given process started from the same shell as this script
# is currently running, false otherwise.
########################################################################
isProcessRunning()
{
    nlines=$(ps -p "$1" | wc -l)
    test "$nlines" -eq 2
}

########################################################################
# Cleanup function
########################################################################
cleanupAll()
{
	cd "${WORK_DIR}"

	cleanupTargets

	if test "t${RECORDER_PID}" != "tERR"; then
		if isProcessRunning ${RECORDER_PID}; then
			kill ${RECORDER_PID}

			# Give the user-space application some time to finish
			sleep 1

			# If it is still running, force it to stop
			if isProcessRunning ${RECORDER_PID}; then
				kill -9 ${RECORDER_PID}
			fi
		fi
	fi
	
	lsmod | grep "${OUTPUT_MODULE_NAME}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${OUTPUT_MODULE_NAME}"
	fi

	lsmod | grep "${FH_COMMON_NAME}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${FH_COMMON_NAME}"
	fi

	lsmod | grep "${FH_CDEV_NAME}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${FH_CDEV_NAME}"
	fi
	
	lsmod | grep "${CORE_MODULE_NAME}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${CORE_MODULE_NAME}"
	fi

	umount "${TEST_DEBUGFS_DIR}"
}

########################################################################
loadCore()
{
	insmod "${CORE_MODULE}" \
		targets="${TARGET_NAME_SAMPLE},${TARGET_NAME_DUMMY},${TARGET_NAME_BUGGY}"
	if test $? -ne 0; then
		printf "Failed to unload the core module: ${CORE_MODULE}\n"
		cleanupAll
		exit 1
	fi
}

unloadCore()
{
	rmmod "${CORE_MODULE_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload the core module.\n"
		cleanupAll
		exit 1
	fi
}

loadFHPlugins()
{
	insmod "${FH_COMMON_MODULE}"
	if test $? -ne 0; then
		printf "Failed to load FH plugin for common operations: ${FH_COMMON_MODULE}.\n"
		cleanupAll
		exit 1
	fi

	insmod "${FH_CDEV_MODULE}"
	if test $? -ne 0; then
		printf "Failed to load FH plugin for cdev operations: ${FH_CDEV_MODULE}.\n"
		cleanupAll
		exit 1
	fi
}

unloadFHPlugins()
{
	rmmod "${FH_COMMON_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${FH_COMMON_NAME}\n"
		cleanupAll
		exit 1
	fi

	rmmod "${FH_CDEV_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload the module: ${FH_CDEV_NAME}\n"
		cleanupAll
		exit 1
	fi
}

########################################################################
loadSampleTarget()
{
	insmod "${TARGET_MODULE_SAMPLE}"
	if test $? -ne 0; then
		printf "Failed to load the target module: ${TARGET_MODULE_SAMPLE}\n"
		cleanupAll
		exit 1
	fi
}

unloadSampleTarget()
{
	lsmod | grep "${TARGET_NAME_SAMPLE}" > /dev/null 2>&1
	if test $? -eq 0; then
		rmmod "${TARGET_NAME_SAMPLE}"
		if test $? -ne 0; then
			printf "Failed to unload the module: ${TARGET_NAME_SAMPLE}\n"
			cleanupAll
			exit 1
		fi

	fi
}

########################################################################
# doTargets() - load, use and unload the target modules
########################################################################
doTargets()
{
	loadSampleTarget
	sleep 1

	ii=0
	while test ${ii} -lt ${NREPEATS}; do
		echo "Abracadabra0987654" > /dev/cfake0
		if test $? -ne 0; then
			printf "Failed to write to the device provided by the target module\n"
			cleanupAll
			exit 1
		fi
		dd if=/dev/cfake0 of=/dev/null bs=30 > /dev/null 2>&1
		if test $? -ne 0; then
			printf "Failed to read from the device provided by the target module\n"
			cleanupAll
			exit 1
		fi

		ii=$((${ii}+1))
	done
	
	unloadSampleTarget
}

########################################################################
# doTest() - perform the actual testing
########################################################################
doTest()
{
	# LZO compression API is used by the output subsystem. Load
	# lzo_compress module just in case it is not built in and is not
	# already loaded. Otherwise, 'modprobe' will be a no-op.
	# This is needed for Debian 6, for example.
	modprobe lzo_compress || exit 1

	loadCore
	loadFHPlugins
	
	insmod "${OUTPUT_MODULE}" nr_data_pages=64
	if test $? -ne 0; then
		printf "Failed to load the kernel-space part of the output system.\n"
		cleanupAll
		exit 1
	fi
	
	"${OUTPUT_RECORDER}" "${TEST_TRACE_RAW_FILE}" &
	RECORDER_PID=$!
	
	if ! isProcessRunning ${RECORDER_PID}; then
		printf "Failed to load the user-space part of the output system.\n"
		cleanupAll
		exit 1
	fi
	
	doTargets
	
	if isProcessRunning ${RECORDER_PID}; then
		# Give the output system some time.
		sleep 1
		
		if isProcessRunning ${RECORDER_PID}; then
			printf "Something wrong happened: "
			printf "the user-space part of the output system is still running.\n"
			cleanupAll
			exit 1
		fi
	fi
	# [NB] The lost events are irrelevant here, so we do not check for
	# them.
	
	# Convert the trace to text format, this may fail if the trace is
	# corrupted, such errors would be therefore caught here.
	"${TRACE_TO_TEXT}" "${TEST_TRACE_RAW_FILE}" > /dev/null
	if test $? -ne 0; then
		printf "Failed to convert the trace to text format.\n"
		cleanupAll
		exit 1
	fi	
	
	# Unload the modules, they are no longer needed
	rmmod "${OUTPUT_MODULE_NAME}"
	if test $? -ne 0; then
		printf "Failed to unload module: ${OUTPUT_MODULE_NAME}\n"
		cleanupAll
		exit 1
	fi
	
	unloadFHPlugins
	unloadCore
}

########################################################################
# main
########################################################################
WORK_DIR=${PWD}

if test $# -ne 0; then
	printf "Usage: sh $0\n"
	exit 1
fi

# How many times to read from and write to the devices managed by the target.
NREPEATS=5000

TOP_STR_DIR="@CMAKE_BINARY_DIR@/utils/simple_trace_recorder"
OUTPUT_MODULE_NAME="kedr_simple_trace_recorder"
OUTPUT_MODULE="${TOP_STR_DIR}/kernel/${OUTPUT_MODULE_NAME}.ko"

OUTPUT_RECORDER="${TOP_STR_DIR}/tests/buffer_wrap/output_user/@RECORDER_TEST_NAME@"
TRACE_TO_TEXT="${TOP_STR_DIR}/tests/trace_to_text/test_trace_to_text"

CORE_MODULE_NAME="kedr_mem_core"
CORE_MODULE="@CMAKE_BINARY_DIR@/core/${CORE_MODULE_NAME}.ko"

TARGET_NAME_SAMPLE="kedr_sample_target"
TARGET_MODULE_SAMPLE="@CMAKE_BINARY_DIR@/tests/sample_target/${TARGET_NAME_SAMPLE}.ko"

FH_COMMON_NAME="kedr_fh_drd_common"
FH_COMMON_MODULE="@CMAKE_BINARY_DIR@/functions/common/${FH_COMMON_NAME}.ko"

FH_CDEV_NAME="kedr_fh_drd_cdev"
FH_CDEV_MODULE="@CMAKE_BINARY_DIR@/functions/cdev/${FH_CDEV_NAME}.ko"

TEST_TMP_DIR="@KEDR_TEST_TEMP_DIR@"
TEST_DEBUGFS_DIR="${TEST_TMP_DIR}/debug"
EVENTS_LOST_FILE="${TEST_DEBUGFS_DIR}/${OUTPUT_MODULE_NAME}/events_lost"

TEST_TRACE_RAW_FILE="${TEST_TMP_DIR}/trace.dat"
TEST_TRACE_TEXT_FILE="${TEST_TMP_DIR}/trace.txt"

RECORDER_PID="ERR"

checkPrereqs

rm -rf "${TEST_TMP_DIR}"
mkdir -p "${TEST_DEBUGFS_DIR}"
if test $? -ne 0; then
	printf "Failed to create ${TEST_DEBUGFS_DIR}\n"
	exit 1
fi

mount -t debugfs none "${TEST_DEBUGFS_DIR}"
if test $? -ne 0; then
	printf "Failed to mount debugfs to ${TEST_DEBUGFS_DIR}\n"
	cleanupAll
	exit 1
fi

doTest

cleanupAll

# test passed
exit 0
